#include <Arduino.h>
#include <U8g2lib.h>
#include "BLEDevice.h"
#include "WiFi.h"
#include <rom/rtc.h>
#include <PubSubClient.h>
#include <Arduino_JSON.h>
#include <esp_wifi.h>
#include "EEPROM.h"
#include "config.h"

// http://www.ithingsboard.com/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial/
// https://docs.espressif.com/projects/esp-idf/zh_CN/stable/esp32/api-reference/system/freertos.html#
// https://github.com/olikraus/u8g2/wiki/fntlist8#u8g2-font-list
// https://blog.csdn.net/dpjcn1990/article/details/92831760
// http://tools.clz.me/image-to-bitmap-array

#define ARDUINO_RUNNING_CORE 1 //内核选择   MQTT用核心0  RTOS用核心1
#define LED_BUILTIN 27

//#define DEBUG_ENABLE 1

U8G2_SSD1306_128X80_NONAME_F_HW_I2C u8g2(U8G2_R3, /* reset=*/U8X8_PIN_NONE, /* clock=*/22, /* data=*/21);

const char *mqtt_server = "39.107.239.44";
String mqtt_clientId = "ESP32_test";
String mqtt_user = "G3Zk244sEgvOh1pFmwmd";
String mqtt_password = "G3Zk244sEgvOh1pFmwmd";
String mqtt_pubtopic = "v1/devices/me/telemetry";     //publish topic
String mqtt_subtopic = "v1/devices/me/rpc/request/+"; //RPC topic

static uint32_t now_timestmp = 0;
static uint32_t timing_timestmp = 0;

WiFiClient espClient;
PubSubClient client(espClient);

// The remote service we wish to connect to.
static BLEUUID serviceUUID("6e400001-b5a3-f393-e0a9-e50e24dcca9e");
static BLEUUID charUUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");
static BLEUUID notifyUUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E");

static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic *pRemoteCharacteristic;
static BLERemoteCharacteristic *pRemotenotifyacteristic;
static BLEAdvertisedDevice *myDevice;
BLEClient *pClient = BLEDevice::createClient();

typedef enum
{
    DEV_NIGHT_MODE,
    DEV_INIT_MODE,
    DEV_WORK_MODE,
    DEV_BLE_WORK,

} DEV_RUNNING_MODE_E;
typedef enum
{
    EVT_WIFI_CONNECT,
    EVT_WIFI_DISCONNECT,
    EVT_MQTT_CONNECT,
    EVT_UNKNOWN,
} EVT_NETWORK_STATUS_E;

static DEV_RUNNING_MODE_E runing_mode = DEV_INIT_MODE;
static EVT_NETWORK_STATUS_E network_status = EVT_UNKNOWN;
int Find_string(char *pcBuf, char *left, char *right, char *pcRes);
void save_ble_control_time(int address, uint32_t timestmp);

//--------------------------RTOS----------------------------------//
//消息队列
struct AMessage
{
    int ucMessageID;
    char ucData[100];
} xMessage;

struct mqtt_message
{
    int ucMessageID;
    char ucData[100];
} Message;

struct ble_scan_msg
{
    char ble_name[20];
    char ble_address[20];
} b_message;

TimerHandle_t network_tmr;       //WIFI连接超时定时器
TimerHandle_t ble_control_tmr;   //BLE控制超时定时器
TimerHandle_t heartbat_send_tmr; //心跳发送定时器
TimerHandle_t ble_countdown_tmr; //BLE控制倒计时定时器   touchRead
TimerHandle_t dev_touchRead_tmr;

QueueHandle_t mqtt_rcv_Queue;  //MQTT 接收消息队列
QueueHandle_t mqtt_send_Queue; //MQTT 发送消息队列
QueueHandle_t ble_send_Queue;  //BLE 发送消息队列
QueueHandle_t ble_scan_Queue;  //BLE 扫描消息队列

TaskHandle_t Task_ble_Handle;  //蓝牙任务，阻塞等待
TaskHandle_t Task_Oled_Handle; //OLED 任务
TaskHandle_t Task_wifi_Handle; //WIFI 任务

void Task_ble(void *pvParameters);
void Task_Oled(void *pvParameters);
void Task_wifi(void *pvParameters);

//--------------------------BLE----------------------------------//
/**
 * @name: 
 * @msg: 通知订阅回调
 * @param {*}
 * @return {*}
 */
static void notifyCallback(
    BLERemoteCharacteristic *pRemotenotifyacteristic,
    uint8_t *pData,
    size_t length,
    bool isNotify)
{
    Serial.print("[D]Notify callback for characteristic ");
    Serial.print(pRemotenotifyacteristic->getUUID().toString().c_str());
    Serial.print(" of data length ");
    Serial.println(length);
    Serial.print("data: ");
    Serial.println((char *)pData);

    JSONVar myObject = JSON.parse((char *)pData);

    // JSON.typeof(jsonVar) can be used to get the type of the var
    if (JSON.typeof(myObject) == "undefined")
    {
        Serial.println("Parsing input failed!");
        return;
    }

    //收到电量数据
    if (myObject.hasOwnProperty("bat"))
    {
        struct mqtt_message *send_message;
        oled_show.ble_battery = (double)myObject["bat"];
        Serial.println(oled_show.ble_battery);

        send_message = &Message;
        memset(send_message->ucData, 0, sizeof(send_message->ucData));
        sprintf(send_message->ucData, "{\"bat\":%.2f}", oled_show.ble_battery);
        xQueueSend(mqtt_send_Queue, (void *)&send_message, (TickType_t)0);

        //停止超时定时器
        xTimerStop(ble_control_tmr, 0);
        runing_mode = DEV_WORK_MODE;
        oled_show.countdown = 0;
    }
}

/**
 * @name: 
 * @msg: BLE 连接回调
 * @param {*}
 * @return {*}
 */
class MyClientCallback : public BLEClientCallbacks
{
    void onConnect(BLEClient *pclient)
    {
        Serial.println("----->connect");
    }

    void onDisconnect(BLEClient *pclient)
    {
        connected = false;
        Serial.println("----->onDisconnect");
        //停止超时定时器
        xTimerStop(ble_control_tmr, 0);
        runing_mode = DEV_WORK_MODE;
        oled_show.countdown = 0;
    }
};

/**
 * @name: bool connectToServer(void)
 * @msg: BLE 连接server
 * @param {*}
 * @return {*}
 */
bool connectToServer(void)
{
    Serial.print("[D]Forming a connection to ");
    Serial.println(myDevice->getAddress().toString().c_str());
    Serial.println(" - Created client");

    pClient->setClientCallbacks(new MyClientCallback());
    pClient->connect(myDevice);
    Serial.println(" - Connected to server");
    BLERemoteService *pRemoteService = pClient->getService(serviceUUID);
    vTaskDelay(500);
    if (pRemoteService == nullptr)
    {
        Serial.print("[E]Failed to find our service UUID: ");
        Serial.println(serviceUUID.toString().c_str());
        pClient->disconnect();
        return false;
    }
    Serial.println(" - Found our service");

    // Obtain a reference to the characteristic in the service of the remote BLE server.
    pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
    if (pRemoteCharacteristic == nullptr)
    {
        Serial.print("[E]Failed to find our characteristic UUID: ");
        Serial.println(charUUID.toString().c_str());
        pClient->disconnect();
        return false;
    }
    Serial.println(" - Found our characteristic");

    //可以读的UUID
    // Read the value of the characteristic.
    if (pRemoteCharacteristic->canRead())
    {
        std::string value = pRemoteCharacteristic->readValue();
        Serial.print("[D]The characteristic value was: ");
        Serial.println(value.c_str());
    }

    //可以订阅的UUID
    pRemotenotifyacteristic = pRemoteService->getCharacteristic(notifyUUID);
    if (pRemotenotifyacteristic->canNotify())
        pRemotenotifyacteristic->registerForNotify(notifyCallback);

    connected = true;
}

/**
 * @name: 
 * @msg: 扫描服务器 并打印相关信息  同时连接对应的设备
 * @param {*}
 * @return {*}
 */
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks
{
    /**
   * Called for each advertising BLE server.
   */
    void onResult(BLEAdvertisedDevice advertisedDevice)
    {
        struct ble_scan_msg *ble_scan_queue;

        char *rcv_buf;
        Serial.print("[D]BLE Advertised Device found: ");
        rcv_buf = (char *)advertisedDevice.toString().c_str();
        Serial.println(rcv_buf);

        ble_scan_queue = &b_message;
        memset(ble_scan_queue->ble_name, 0, sizeof(ble_scan_queue->ble_name));
        memset(ble_scan_queue->ble_address, 0, sizeof(ble_scan_queue->ble_address));
        Find_string(rcv_buf, "Name:", ",", ble_scan_queue->ble_name);
        Find_string(rcv_buf, ", Address:", ",", ble_scan_queue->ble_address);
        Serial.print(ble_scan_queue->ble_name);
        Serial.println(ble_scan_queue->ble_address);
        xQueueSend(ble_scan_Queue, (void *)&ble_scan_queue, (TickType_t)0);

        // We have found a device, let us now see if it contains the service we are looking for.
        if (strstr(ble_scan_queue->ble_address, dev_info.ble_control_mac) != NULL)
        {
            if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID))
            {
                BLEDevice::getScan()->stop();
                myDevice = new BLEAdvertisedDevice(advertisedDevice);
                doConnect = true;
                doScan = true;
            }
        }
    }
};

/**
 * @name: void ble_control_timeout(TimerHandle_t pxTimer)
 * @msg: BLE 控制超时，实现重发，目前存在bug
 * @param {TimerHandle_t} pxTimer
 * @return {*}
 */
void ble_control_timeout(TimerHandle_t pxTimer)
{
    int32_t lArrayIndex;
    configASSERT(pxTimer);
    // 读取超时定时器的ID
    lArrayIndex = (int32_t)pvTimerGetTimerID(pxTimer);
    Serial.print("[E]ble_control_timeout----");
    Serial.println(lArrayIndex);

    xTimerStop(pxTimer, 0);

    //断开连接
    doConnect = false;
    doScan = false;
    pClient->disconnect();

    //重试
    // ble_control(false);
}

//--------------------------WIFI-MQTT----------------------------------//
/**
 * @name: void network_connect_timeout(TimerHandle_t pxTimer)
 * @msg: 网络连接超时
 * @param {TimerHandle_t} pxTimer
 * @return {*}
 */
void network_connect_timeout(TimerHandle_t pxTimer)
{
    int32_t lArrayIndex;
    configASSERT(pxTimer);
    // 读取超时定时器的ID
    lArrayIndex = (int32_t)pvTimerGetTimerID(pxTimer);
    Serial.print("[E]network_connect_timeout----");
    Serial.println(lArrayIndex);

    ESP.restart();
    //xTimerStop( pxTimer, 0 );
}

/**
 * @name: void mqtt_callback(char *topic, byte *payload, unsigned int length)
 * @msg: MQTT数据接收回调
 * @param {char} *topic
 * @param {byte} *payload
 * @param {unsigned int} length
 * @return {*}
 */
void mqtt_callback(char *topic, byte *payload, unsigned int length)
{
    struct AMessage *rcv_Message;
    char rcv_buff[length + 1];

    Serial.print("[D]Message arrived [");
    Serial.print(topic);
    Serial.print("] ");
    memset(rcv_buff, 0, sizeof(rcv_buff));
    for (int i = 0; i < length; i++)
    {
        rcv_buff[i] = (char)payload[i];
    }
    Serial.println(rcv_buff);

    rcv_Message = &xMessage;
    rcv_Message->ucMessageID = atoi(topic + 26);
    memset(rcv_Message->ucData, 0, sizeof(rcv_Message->ucData));
    strncpy(rcv_Message->ucData, rcv_buff, length);

    Serial.print("[D]-----------------callback-------------------");
    Serial.println(rcv_Message->ucData);

    xQueueSend(mqtt_rcv_Queue, (void *)&rcv_Message, (TickType_t)0);
}

/**
 * @name: void reconnect(void)
 * @msg: MQTT 连接与重连
 * @param {*}
 * @return {*}
 */
void reconnect(void)
{
    // Loop until we're reconnected
    static int unt = 0;
    while (!client.connected())
    {
        Serial.print("[D]Attempting MQTT connection...");
        memset(oled_show.mqtt_connect_flag, 0, sizeof(oled_show.mqtt_connect_flag));
        sprintf(oled_show.mqtt_connect_flag, "%s", "loading...");

        // Attempt to connect  id username  password
        if (client.connect(mqtt_clientId.c_str(), dev_info.esp32_chipid, dev_info.esp32_chipid))
        {
            Serial.println("connected");
            sprintf(oled_show.mqtt_connect_flag, "%s", "connected!");
            unt = 0;

            // ... and resubscribe
            client.subscribe(mqtt_subtopic.c_str());
        }
        else
        {
            Serial.print("[E]failed, rc=");
            Serial.print(client.state());
            Serial.printf(" try again in %d seconds", 6 - unt);
            sprintf(oled_show.mqtt_connect_flag, "%s", "reconnect...");
            unt++;
            if (unt >= 6)
            {
                ESP.restart();
            }
            // Wait 5 seconds before retrying
            vTaskDelay(5000);
        }
    }
}

void ble_control(bool countdown)
{
    struct AMessage *send_Message;
    send_Message = &xMessage;
    send_Message->ucMessageID = 6;
    sprintf(send_Message->ucData, "%s", "OPEN");
    xQueueSend(ble_send_Queue, (void *)&send_Message, (TickType_t)0);
    //开启超时定时器
    xTimerStart(ble_control_tmr, 0);
}

/**
 * @name: void mqtt_data_parse(mqtt_message *mqtt_data)
 * @msg:  MQTT 数据解析
 * @param {mqtt_message} *mqtt_data
 * @return {*}
 */
void mqtt_data_parse(mqtt_message *mqtt_data)
{
    Serial.println();
    Serial.println("-------------------rcv------------------------");
    Serial.println(mqtt_data->ucMessageID);
    Serial.println(mqtt_data->ucData);

    JSONVar myObject = JSON.parse((char *)mqtt_data->ucData);

    if (JSON.typeof(myObject) == "undefined")
    {
        Serial.println("Parsing input failed!");
        return;
    }

    //收到RPC 数据
    if (myObject.hasOwnProperty("method"))
    {
        JSONVar inobject = myObject["params"];
        now_timestmp = time2Stamp();
        Serial.println(JSON.typeof(inobject));
        memset(oled_show.mqtt_rcv, 0, sizeof(oled_show.mqtt_rcv));
        memset(dev_info.ble_control_mac, 0, sizeof(dev_info.ble_control_mac));
        if (myObject.hasOwnProperty("params"))
        {
            sprintf(oled_show.mqtt_rcv, "%s", JSON.stringify(inobject).c_str());
        }
        else
        {
            goto exit;
        }

        if (inobject.hasOwnProperty("c"))
        {
            sprintf(dev_info.ble_control_mac, "%s", (char *)(const char *)inobject["c"]);
        }
        else
        {
            goto exit;
        }

        if (((int)inobject["h"] == 0) && ((int)inobject["m"] == 0))
        {
            Serial.println("Executed immediately !!!!");
            ble_control(true);
        }
        else if (mqtt_data->ucMessageID != load_ble_control_time(ble_control_time_address, true)) //过滤掉保留消息
        {
            Serial.println("Countdown execution !!!!");
            if ((int)inobject["h"] != 0)
            {
                save_ble_control_time(ble_control_time_address, mqtt_data->ucMessageID, now_timestmp + 3600 * (int)inobject["h"]);
            }
            else if ((int)inobject["m"] != 0)
            {
                save_ble_control_time(ble_control_time_address, mqtt_data->ucMessageID, now_timestmp + 60 * (int)inobject["m"]);
            }
        }
        else
        {
            Serial.printf("[W]ignore message id: !!!!\r\n", "%d", mqtt_data->ucMessageID);
        }
    }
exit:
    return;
}

/**
 * @name: void heartbat_send_timeout(TimerHandle_t pxTimer)
 * @msg: 
 * @param {TimerHandle_t} pxTimer
 * @return {*}
 */
void heartbat_send_timeout(TimerHandle_t pxTimer)
{
    int32_t lArrayIndex;
    struct mqtt_message *send_message;
    configASSERT(pxTimer);
    // 读取超时定时器的ID
    lArrayIndex = (int32_t)pvTimerGetTimerID(pxTimer);
    Serial.print("[D]heartbat_send_timeout----");
    Serial.println(lArrayIndex);

    local_time_config();
    send_message = &Message;
    memset(send_message->ucData, 0, sizeof(send_message->ucData));
    strcpy(send_message->ucData, "{\"heabart\":200}");
    xQueueSend(mqtt_send_Queue, (void *)&send_message, (TickType_t)0);

    //ESP.restart();
    //xTimerStop( pxTimer, 0 );
}

/**
 * @name: void ble_countdown_timeout(TimerHandle_t pxTimer)
 * @msg: 
 * @param {TimerHandle_t} pxTimer
 * @return {*}
 */
void ble_countdown_timeout(TimerHandle_t pxTimer)
{

    int32_t lArrayIndex;
    configASSERT(pxTimer);
    // 读取超时定时器的ID
    lArrayIndex = (int32_t)pvTimerGetTimerID(pxTimer);
    Serial.print("[D]ble_countdown_timeout----");
    Serial.println(lArrayIndex);

    //当前时间戳大于定时的时间戳 执行定时任务
    now_timestmp = time2Stamp();
    timing_timestmp = load_ble_control_time(ble_control_time_address, false);

    if (timing_timestmp != 0)
    {
        if (now_timestmp >= timing_timestmp)
        {
            xTimerReset(ble_countdown_tmr, 0);
            ble_control(true);
        }
        else
        {
            //显示倒计时：
            oled_show.countdown = timing_timestmp - now_timestmp;
        }
    }
}

/**
 * @name: void local_time_config(void)
 * @msg: 
 * @param {*}
 * @return {*}
 */
void local_time_config(void)
{
    configTime(3600 * 7, 3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
    struct tm tmstruct;
    tmstruct.tm_year = 0;
    getLocalTime(&tmstruct, 5000);
    Serial.printf("\nNow is : %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, tmstruct.tm_sec);
    Serial.println("");
    //time2Stamp();
}

//--------------------------HAL----------------------------------//

void verbose_print_reset_reason(RESET_REASON reason)
{
    switch (reason)
    {
    case 1:
        Serial.println("Vbat power on reset");
        break;
    case 3:
        Serial.println("Software reset digital core");
        break;
    case 4:
        Serial.println("Legacy watch dog reset digital core");
        break;
    case 5:
        Serial.println("Deep Sleep reset digital core");
        break;
    case 6:
        Serial.println("Reset by SLC module, reset digital core");
        break;
    case 7:
        Serial.println("Timer Group0 Watch dog reset digital core");
        break;
    case 8:
        Serial.println("Timer Group1 Watch dog reset digital core");
        break;
    case 9:
        Serial.println("RTC Watch dog Reset digital core");
        break;
    case 10:
        Serial.println("Instrusion tested to reset CPU");
        break;
    case 11:
        Serial.println("Time Group reset CPU");
        break;
    case 12:
        Serial.println("Software reset CPU");
        break;
    case 13:
        Serial.println("RTC Watch dog Reset CPU");
        break;
    case 14:
        Serial.println("for APP CPU, reseted by PRO CPU");
        break;
    case 15:
        Serial.println("Reset when the vdd voltage is not stable");
        break;
    case 16:
        Serial.println("RTC Watch dog reset digital core and rtc module");
        break;
    default:
        Serial.println("NO_MEAN");
    }
}

/**
 * @name: void dev_touchRead_timeout(TimerHandle_t pxTimer)
 * @msg: 
 * @param {TimerHandle_t} pxTimer
 * @return {*}
 */
void dev_touchRead_timeout(TimerHandle_t pxTimer)
{
    configASSERT(pxTimer);
#ifdef DEBUG_ENABLE
    int32_t lArrayIndex;
    // 读取超时定时器的ID
    lArrayIndex = (int32_t)pvTimerGetTimerID(pxTimer);
    Serial.print("dev_touchRead_timeout----");
    Serial.println(lArrayIndex);
#endif
    static int touch_num = 0;
    oled_show.touchread_num = touchRead(T0);

    if (oled_show.touchread_num <= 40)
    {
        touch_num++;

        if ((touch_num % (1000 / 50)) == 0)
        {
            oled_show.longtouch_flag++;
        }

        if (touch_num >= 5000 / 50)
            ESP.restart();
    }
    else
    {
        touch_num = 0;
        oled_show.longtouch_flag = 0;
    }
}

/**
 * @name: 
 * @msg: 
 * @param {char} *localtime
 * @return {*}
 */
void get_localtime(char *localtime)
{
    struct tm tmstruct;
    getLocalTime(&tmstruct, 5000);
    sprintf(localtime, "%02d:%02d:%02d", tmstruct.tm_hour, tmstruct.tm_min, tmstruct.tm_sec);

    if (tmstruct.tm_hour <= 8)
    {
        oled_show.night_mode = true;
    }
    else
    {
        oled_show.night_mode = false;
    }
}
/**
 * @name: void save_ble_control_time(int address, uint32_t timestmp)
 * @msg: 
 * @param {int} address
 * @param {uint32_t} timestmp
 * @return {*}
 */
void save_ble_control_time(int address, int id, uint32_t timestmp)
{
    JSONVar myObject;
    myObject["id"] = (int)id;
    myObject["timestmp"] = (int)timestmp;
    String jsonString = JSON.stringify(myObject);
    EEPROM.writeString(address, jsonString);
    EEPROM.commit();
    Serial.print("save_ble_control_time success:");
    Serial.println(EEPROM.readString(address));
}

uint32_t load_ble_control_time(int address, bool time_id)
{
    char e2p_info[100];
    char *buf;
    int timestmp = 0;
    buf = (char *)EEPROM.readString(address).c_str();
    memset(e2p_info, 0, sizeof(e2p_info));
    snprintf(e2p_info, strlen(buf) + 1, "%s", buf);
    Serial.print("load_ble_control_time success: ");
    Serial.println(e2p_info);

    JSONVar myObject = JSON.parse(e2p_info);
    if (JSON.typeof(myObject) == "undefined")
    {
        Serial.println("Parsing input failed!");
        return 0;
    }
    //读取ID
    if (time_id)
    {
        if (myObject.hasOwnProperty("id"))
        {
            timestmp = (int)myObject["id"];
            return (uint32_t)timestmp;
        }
    }
    //读取时间戳
    if (myObject.hasOwnProperty("timestmp"))
    {
        timestmp = (int)myObject["timestmp"];
        Serial.println((int)myObject["timestmp"]);
        return (uint32_t)timestmp;
    }
}

void save_wifi_info(int address, char *ssid, char *psk)
{
    JSONVar myObject;
    myObject["ssid"] = ssid;
    myObject["psk"] = psk;
    String jsonString = JSON.stringify(myObject);
    EEPROM.writeString(address, jsonString);
    EEPROM.commit();
    Serial.print("save_wifi_info success:");
    Serial.println(EEPROM.readString(address));
}

//void load_wifi_info(int address, char **ssid, char **pssk)
void load_wifi_info(int address)
{
    char wifi_info[100];
    char *buf;

    memset(wifi_info, 0, sizeof(wifi_info));
    buf = (char *)EEPROM.readString(address).c_str();
    EEPROM.commit();
    snprintf(wifi_info, strlen(buf) + 1, "%s", buf);
    Serial.println(wifi_info);

    JSONVar myObject = JSON.parse(wifi_info);
    if (JSON.typeof(myObject) == "undefined")
    {
        Serial.println("Parsing input failed!");
        return;
    }
    if (myObject.hasOwnProperty("ssid") && myObject.hasOwnProperty("psk"))
    {
        Serial.println((const char *)myObject["ssid"]);
        Serial.println((const char *)myObject["psk"]);

        sprintf(dev_info.wifi_ssid, "%s", (char *)(const char *)myObject["ssid"]);
        sprintf(dev_info.wifi_password, "%s", (char *)(const char *)myObject["psk"]);
    }
}

/**
 * @name: void wifi_connent(void)
 * @msg: WIFI连接
 * @param {*}
 * @return {*}
 */
void wifi_connent(void)
{
    static int connent_time = 0;

    xTimerStart(network_tmr, 0); //开启联网超时定时器
    load_wifi_info(config_address);
    Serial.printf("---%s--%s---\r\n", dev_info.wifi_ssid, dev_info.wifi_password);
    WiFi.begin(dev_info.wifi_ssid, dev_info.wifi_password);
    while (WiFi.status() != WL_CONNECTED)
    {
        vTaskDelay(500);
        Serial.print(".");
        connent_time++;
        if (connent_time > 60)
        {
            Serial.println("WIFI Wait for Smartconfig");
            //设置为Station模式
            WiFi.mode(WIFI_STA);
            //开始智能配网
            WiFi.beginSmartConfig();
            while (1)
            {
                //智能配网成功
                if (WiFi.smartConfigDone())
                {
                    WiFi.setAutoConnect(true); // 设置自动连接
                    connent_time = 0;
                    break;
                }
                vTaskDelay(500);
            }
        }
    }
    //连接成功,打印WIFI信息
    Serial.println("");
    Serial.println("WIFI SmartConfig Success");
    Serial.printf("SSID:%s", WiFi.SSID().c_str());
    Serial.printf(", PSW:%s\r\n", WiFi.psk().c_str());
    save_wifi_info(config_address, (char *)WiFi.SSID().c_str(), (char *)WiFi.psk().c_str());
    Serial.print("LocalIP:");
    Serial.print(WiFi.localIP());
    Serial.print(" ,GateIP:");
    Serial.println(WiFi.gatewayIP());

    client.setServer(mqtt_server, 1883);
    client.setCallback(mqtt_callback);

    network_status = EVT_WIFI_CONNECT;
    xTimerStop(network_tmr, 0);           //联网成功，关闭超时定时器
    xTimerStart(heartbat_send_tmr, 5000); //开启心跳发送定时器
}

/**
 * @name: void network_setup(void)
 * @msg: 网络相关初始化
 * @param {*}
 * @return {*}
 */
void network_setup(void)
{
    //创建WIFI连接超时定时器-20S
    network_tmr = xTimerCreate("network_connect_Timer",
                               (1000 * 100),
                               pdTRUE,
                               (void *)0,
                               network_connect_timeout);

    //创建BLE控制超时定时器-15S
    ble_control_tmr = xTimerCreate("ble_control_Timer",
                                   (1000 * 60),
                                   pdTRUE,
                                   (void *)0,
                                   ble_control_timeout);

    //创建BLE控制超时定时器-15S
    heartbat_send_tmr = xTimerCreate("heartbat_send_Timer",
                                     (1000 * 60),
                                     pdTRUE,
                                     (void *)0,
                                     heartbat_send_timeout);

    //创建BLE控制倒计时定时器
    ble_countdown_tmr = xTimerCreate("ble_countdown_Timer",
                                     (1000),
                                     pdTRUE,
                                     (void *)0,
                                     ble_countdown_timeout);
    xTimerStart(ble_countdown_tmr, 0); //开启倒计时定时器

    //创建touchread定时器
    dev_touchRead_tmr = xTimerCreate("dev_touchRead_Timer",
                                     (50),
                                     pdTRUE,
                                     (void *)0,
                                     dev_touchRead_timeout);

    xTimerStart(dev_touchRead_tmr, 0); //开启联网超时定时器

    mqtt_rcv_Queue = xQueueCreate(10, sizeof(struct mqtt_message *));
    mqtt_send_Queue = xQueueCreate(10, sizeof(struct mqtt_message *));
    ble_send_Queue = xQueueCreate(10, sizeof(struct AMessage *));
    ble_scan_Queue = xQueueCreate(10, sizeof(struct ble_scan_msg *));
}

/**
 * @name: void hal_init(void)
 * @msg: hal相关初始化
 * @param {*}
 * @return {*}
 */
void hal_init(void)
{
    Serial.begin(115200);
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, HIGH);

    Serial.print("CPU0 reset reason:");
    verbose_print_reset_reason(rtc_get_reset_reason(0));
    Serial.print("CPU1 reset reason:");
    verbose_print_reset_reason(rtc_get_reset_reason(1));
    Serial.printf("get Chip Revision = %u\n", ESP.getChipRevision());
    Serial.printf("getCpuFreqMHz = %u\n", ESP.getCpuFreqMHz());
    Serial.printf("get Cycle Count = %u\n", ESP.getCycleCount());
    Serial.printf("get SdkVersion = %s\n", ESP.getSdkVersion());

    // uint64_t chipid;
    // chipid = ESP.getEfuseMac();  经测试 并不是唯一的
    // Serial.printf("ESP32 Chip ID = %04X-%08X\r\n", (uint16_t)(chipid >> 32), (uint64_t)chipid);
    memset(dev_info.esp32_chipid, 0, sizeof(dev_info.esp32_chipid));
    sprintf(dev_info.esp32_chipid, "ESP%s", (char *)WiFi.macAddress().c_str());
    Serial.println(dev_info.esp32_chipid);

    if (!EEPROM.begin(1000))
    {
        Serial.println("Failed to initialise EEPROM");
        Serial.println("Restarting...");
        delay(1000);
        ESP.restart();
    }

    u8g2.begin();
    u8g2.enableUTF8Print();
}

/**
 * @name: void setup()
 * @msg: 
 * @param {*}
 * @return {*}
 */
void setup()
{
    hal_init();
    network_setup();

    xTaskCreatePinnedToCore(
        Task_ble, "Task_ble", 4096, NULL, 2, &Task_ble_Handle, ARDUINO_RUNNING_CORE);

    xTaskCreatePinnedToCore(
        Task_Oled, "Task_Oled", 2048, NULL, 1, &Task_Oled_Handle, ARDUINO_RUNNING_CORE);

    xTaskCreatePinnedToCore(
        Task_wifi, "Task_wifi", 4096, NULL, 2, &Task_wifi_Handle, ARDUINO_RUNNING_CORE);
}

//内核0 单独维护MQTT网络
void loop()
{
    if (EVT_WIFI_CONNECT == network_status)
    {
        if (!client.connected())
        {
            reconnect();
        }
        client.loop();
    }
}

void Task_ble(void *pvParameters)
{
    (void)pvParameters;
    struct AMessage *pxRxedMessage;

    for (;;)
    {

        if (xQueueReceive(ble_send_Queue, &(pxRxedMessage), (TickType_t)portMAX_DELAY)) //阻塞等待消息
        {
            save_ble_control_time(ble_control_time_address, 999, 0);

            runing_mode = DEV_BLE_WORK;
            Serial.println(pxRxedMessage->ucData);
            Serial.println("Starting Arduino BLE Client application...");
            BLEDevice::init("");
            BLEScan *pBLEScan = BLEDevice::getScan();
            pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
            pBLEScan->setInterval(1349); //扫描间隔
            pBLEScan->setWindow(449);    //扫描窗口
            pBLEScan->setActiveScan(true);
            pBLEScan->start(30, false); //扫描时间

            while (1)
            {
                if (doConnect == true)
                {
                    if (connectToServer())
                    {
                        Serial.println("We are now connected to the BLE Server.");
                    }
                    else
                    {
                        Serial.println("We have failed to connect to the server; there is nothin more we will do.");
                    }
                    doConnect = false;
                }

                if (connected)
                {
                    String newValue = "CLOSE";
                    Serial.println("Setting new characteristic value to \"" + newValue + "\"");

                    // Set the characteristic's value to be the array of bytes that is actually a string.
                    pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
                    vTaskDelay(2000); //等待消息送达
                    pClient->disconnect();
                    break;
                }
                else if (doScan)
                {
                    BLEDevice::getScan()->start(0); // this is just eample to start scan after disconnect, most likely there is better way to do it in arduino
                    //break;
                }

                vTaskDelay(100);
            }
        }
    }
}

void show_wifi_config(void)
{
    u8g2.clearBuffer();
    u8g2.setCursor(3, 45);
    u8g2.print("扫码");
    u8g2.setCursor(3, 65);
    u8g2.print("配网");
    u8g2.drawXBMP(3, 0, 32, 32, wifi_logo);
    u8g2.drawXBMP(45, 0, 80, 80, config_logo);
    u8g2.sendBuffer();
}

/**
 * @name: void show_HighWater(uint8_t x, uint8_t y)
 * @msg: 显示任务 栈空间剩余
 * @param {uint8_t} x 块区域基准x
 * @param {uint8_t} y 块区域基准y
 * @return {*}
 */
void show_HighWater(uint8_t x, uint8_t y)
{
    static int HighWater_l = 0;

    HighWater_l = 60 * oled_show.ble_HighWater / 4096;
    u8g2.drawBox(x, y, HighWater_l, 8);
    u8g2.setFont(u8g2_font_5x7_mn);
    u8g2.setCursor(HighWater_l + 4, y);
    u8g2.print(oled_show.ble_HighWater);

    HighWater_l = 60 * oled_show.oled_HighWater / 4096;
    u8g2.drawBox(x, y + 10, HighWater_l, 8);
    u8g2.setCursor(HighWater_l + 4, y + 10);
    u8g2.print(oled_show.oled_HighWater);

    HighWater_l = 60 * oled_show.wifi_HighWater / 4096;
    u8g2.drawBox(x, y + 20, HighWater_l, 8);
    u8g2.setCursor(HighWater_l + 4, y + 20);
    u8g2.print(oled_show.wifi_HighWater);

    u8g2.drawBox(x, y + 30, oled_show.touchread_num, 4);
    u8g2.setCursor(oled_show.touchread_num + 4, y + 28);
    u8g2.print(oled_show.longtouch_flag);
}

/**
 * @name: void show_myui(void)
 * @msg: 基础UI显示
 * @param {*}
 * @return {*}
 */
void show_myui(void)
{
    char localtime[9];
    get_localtime(localtime);

    u8g2.drawLine(3, 20, 125, 20);
    u8g2.drawLine(80, 20, 80, 70);
    u8g2.setFont(u8g2_font_unifont_t_symbols);
    u8g2.setFontPosTop();
    u8g2.drawUTF8(3, 10, "☁");
    u8g2.setFont(u8g2_font_5x7_tr);
    u8g2.setCursor(20, 10);
    u8g2.print("MQTT");
    u8g2.setCursor(50, 10);
    u8g2.print(localtime);
    u8g2.setCursor(110, 10);
    u8g2.print("BLE");
    u8g2.setCursor(85, 22);
    u8g2.print("P:     V");

    u8g2.setFont(u8g2_font_5x7_tr);
    u8g2.setCursor(3, 22);
    u8g2.print(oled_show.mqtt_connect_flag);

    u8g2.setFont(u8g2_font_5x7_tr);
    u8g2.setCursor(3, 30);
    u8g2.print(oled_show.mqtt_rcv);

    u8g2.setFont(u8g2_font_5x7_mn);
    u8g2.setCursor(95, 22);
    u8g2.print(oled_show.ble_battery);

    if (oled_show.countdown < 100)
    {
        u8g2.setFont(u8g2_font_10x20_mn);
        u8g2.setCursor(95, 35);
    }
    else if (oled_show.countdown < 999)
    {
        u8g2.setFont(u8g2_font_10x20_mn);
        u8g2.setCursor(90, 35);
    }
    else
    {
        u8g2.setFont(u8g2_font_5x7_mn);
        u8g2.setCursor(85, 45);
    }

    u8g2.print(oled_show.countdown);
}

/**
 * @name: void show_ble_work(void)
 * @msg: 
 * @param {*}
 * @return {*}
 */
void show_ble_work(void)
{
    static int line_num = 1;
    u8g2.setFont(u8g2_font_unifont_t_chinese1);

    if (line_num)
        u8g2.clearBuffer();

    u8g2.drawFrame(3, 3, 120, 70);
    struct ble_scan_msg *pxRxedMessage;
    if (xQueueReceive(ble_scan_Queue, &(pxRxedMessage), (TickType_t)10))
    {
        Serial.print("*****");
        Serial.println(pxRxedMessage->ble_name);
        //Serial.println(pxRxedMessage->ble_address);
        u8g2.setFont(u8g2_font_5x7_tr);
        u8g2.setCursor(5, 8 * line_num);
        u8g2.print(pxRxedMessage->ble_address);
        line_num++;
    }

    if (line_num == 10)
        u8g2.sendBuffer();
}

void Task_Oled(void *pvParameters)
{
    (void)pvParameters;

    u8g2.setFont(u8g2_font_unifont_t_chinese1);
    u8g2.setFontDirection(0);

    for (;;)
    {
        switch (runing_mode)
        {
        case DEV_INIT_MODE:
            show_wifi_config();
            break;
        case DEV_WORK_MODE:
            u8g2.clearBuffer();
            show_myui();
            show_HighWater(3, 40);
            u8g2.sendBuffer();
            break;
        case DEV_BLE_WORK:
            show_ble_work();

            break;

        default:
            break;
        }
    }
}

void Task_wifi(void *pvParameters)
{
    (void)pvParameters;

    for (;;)
    {
        switch (runing_mode)
        {
        case DEV_INIT_MODE:
            wifi_connent();
            local_time_config();

            runing_mode = DEV_WORK_MODE;
            break;

        case DEV_NIGHT_MODE:
            break;

        case DEV_BLE_WORK:
        case DEV_WORK_MODE:

            oled_show.ble_HighWater = uxTaskGetStackHighWaterMark(Task_ble_Handle);
            oled_show.oled_HighWater = uxTaskGetStackHighWaterMark(Task_Oled_Handle);
            oled_show.wifi_HighWater = uxTaskGetStackHighWaterMark(Task_wifi_Handle);

            struct mqtt_message *pxRxedMessage;
            if (xQueueReceive(mqtt_rcv_Queue, &(pxRxedMessage), (TickType_t)10))
            {
                mqtt_data_parse(pxRxedMessage);
            }

            struct mqtt_message *sendMessage;
            if (xQueueReceive(mqtt_send_Queue, &(sendMessage), (TickType_t)10))
            {
                client.publish(mqtt_pubtopic.c_str(), sendMessage->ucData);
            }

            if (oled_show.night_mode == false)
            {
                digitalWrite(LED_BUILTIN, HIGH);
                vTaskDelay(100);
                digitalWrite(LED_BUILTIN, LOW);
                vTaskDelay(100);
            }
            else
            {
                digitalWrite(LED_BUILTIN, HIGH);
            }
            break;

        default:
            break;
        }
    }
}

/***********************************************************
  函数名称：Find_string(char *pcBuf,char*left,char*right, char *pcRes)
  函数功能：寻找特定字符串
  入口参数：
           char *pcBuf 为传入的字符串
           char*left   为搜索字符的左边标识符  例如："["
           char*right  为搜索字符的右边标识符  例如："]"
					 char *pcRes 为输出转存的字符串数组
  返回值：用来校验是否成功，无所谓的。
  备注： left字符需要唯一，right字符从left后面开始唯一即可
 服务器下发命令举例：+MQTTPUBLISH: 0,0,0,0,/device/NB/zx99999999999999_back,6,[reastrobot]
***********************************************************/
int Find_string(char *pcBuf, char *left, char *right, char *pcRes)
{
    char *pcBegin = NULL;
    char *pcEnd = NULL;
    pcBegin = strstr(pcBuf, left);                 //找到第一次出现的位置
    pcEnd = strstr(pcBegin + strlen(left), right); //找到右边标识
    if (pcBegin == NULL || pcEnd == NULL || pcBegin > pcEnd)
    {
        Serial.printf("string name not found!\n");
        return 0;
    }
    else
    {
        pcBegin += strlen(left);
        memcpy(pcRes, pcBegin, pcEnd - pcBegin);
        return 1;
    }
}

unsigned long time2Stamp(void) //北京时间转时间戳
{
    unsigned long Timestamp = 0;
    struct tm stm = {0};
    getLocalTime(&stm, 5000);

    Timestamp = mktime(&stm); //转换为时间戳（上报平台的时间戳需要后面加3个0)
    Serial.printf("*RTC*********%ld\r\n", Timestamp);
    return Timestamp;
}

/*

OLED 5S 刷新一次？
配网慢，且不能掉电记忆！！

*/
